package com.flow.framework.module.call.config;

import com.flow.framework.core.service.properties.ISystemConfigPropertiesService;
import com.flow.framework.module.call.aspect.ForbidDistributedTransactionAspect;
import com.flow.framework.module.call.properties.FrameworkModuleCallConfigProperties;
import com.flow.framework.module.call.rpc.builder.FeignBuilder;
import com.flow.framework.module.call.rpc.client.DistributedTransactionClient;
import com.flow.framework.module.call.rpc.decoder.CustomizationRpcFailedDecoder;
import com.flow.framework.module.call.rpc.decoder.CustomizationRpcSuccessDecoder;
import com.flow.framework.module.call.rpc.decoder.RpcHelper;
import com.flow.framework.module.call.rpc.encoder.CustomizationRpcEncoder;
import com.flow.framework.module.call.rpc.factory.FeignInvocationHandlerFactory;
import com.flow.framework.module.call.rpc.interceptor.FeignCommonInterceptor;
import com.flow.framework.module.call.rpc.interceptor.FeignRequestParamsInterceptor;
import com.flow.framework.module.call.rpc.interceptor.FeignSecurityContextInterceptor;
import com.flow.framework.module.call.rpc.manager.FeignManager;
import com.flow.framework.module.call.rpc.message.FeignObjectMessageConverter;
import feign.Client;
import feign.Feign;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import org.apache.http.client.HttpClient;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.core.env.Environment;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;

/**
 * 框架RPC配置
 *
 * @author luoguopiao
 * @version 0.0.1
 * @date 2022/2/19
 */
@Configuration
public class FrameworkModuleCallConfig {

    private ObjectFactory<HttpMessageConverters> messageConverters = () ->
            new HttpMessageConverters(
                    new ByteArrayHttpMessageConverter(),
                    new FeignObjectMessageConverter());

    @Bean
    @ConditionalOnMissingBean
    ForbidDistributedTransactionAspect forbidDistributedTransactionAspect() {
        return new ForbidDistributedTransactionAspect();
    }

    @Bean
    @ConditionalOnMissingBean(Client.class)
    Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
                       SpringClientFactory clientFactory, HttpClient httpClient,
                       FeignManager feignManager) {
        DistributedTransactionClient delegate = new DistributedTransactionClient(httpClient, feignManager);
        return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
    }

    @Bean
    @ConditionalOnMissingBean
    @RefreshScope
    @ConfigurationProperties(prefix = "customization.framework.module.call")
    FrameworkModuleCallConfigProperties frameworkModuleCallConfigProperties() {
        return new FrameworkModuleCallConfigProperties();
    }

    @Bean
    @ConditionalOnMissingBean
    Retryer feignRetryer() {
        return Retryer.NEVER_RETRY;
    }

    @Bean
    @ConditionalOnMissingBean
    FeignManager feignManager(Environment environment, FrameworkModuleCallConfigProperties frameworkModuleCallConfigProperties) {
        return new FeignManager(environment, frameworkModuleCallConfigProperties);
    }

    @Bean
    @ConditionalOnMissingBean
    RpcHelper rpcHelper() {
        return new RpcHelper();
    }

    @Bean
    @ConditionalOnMissingBean
    FeignCommonInterceptor feignCommonInterceptor(ISystemConfigPropertiesService systemConfigPropertiesService, FeignManager feignManager) {
        return new FeignCommonInterceptor(systemConfigPropertiesService, feignManager);
    }

    @Bean
    @ConditionalOnMissingBean
    FeignRequestParamsInterceptor feignRequestParamsInterceptor() {
        return new FeignRequestParamsInterceptor();
    }

    @Bean
    @ConditionalOnMissingBean
    FeignSecurityContextInterceptor feignSecurityContextInterceptor(FeignManager feignManager) {
        return new FeignSecurityContextInterceptor(feignManager);
    }

    /**
     * 自定义Feign.Builder，方便对feign的客户端进行集中管理
     *
     * @param feignManager feignManager
     * @param retryer      retryer
     * @return Builder
     */
    @Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    Feign.Builder feignBuilder(FeignManager feignManager, Retryer retryer) {
        return new FeignBuilder(feignManager).invocationHandlerFactory(new FeignInvocationHandlerFactory()).retryer(retryer);
    }

    @Bean
    @ConditionalOnMissingBean
    Decoder feignDecoder() {
        return new CustomizationRpcSuccessDecoder(messageConverters);
    }

    @Bean
    @ConditionalOnMissingBean
    Encoder feignEncoder() {
        return new CustomizationRpcEncoder(messageConverters);
    }

    @Bean
    @ConditionalOnMissingBean
    ErrorDecoder errorDecoder() {
        return new CustomizationRpcFailedDecoder();
    }
}
